构造函数初始化列表
构造函数的执行分为两个阶段
- 初始化段
- 普通计算段
在普通计算段中,其实是赋值操作
,推荐在构造函数初始化列表中进行初始化
#include <iostream>
using namespace std;
class Object
{
public:
Object(int num) : num_(num)
{
cout << "Object " << num_ << " ..." << endl;
}
~Object()
{
cout << "~Object " << num_ << " ..." << endl;
}
private:
int num_;
};
class Container
{
public:
Container(int obj1 = 0, int obj2 = 0) : obj2_(obj2), obj1_(obj1)
{
cout << "Container ..." << endl;
}
~Container()
{
cout << "~Container ..." << endl;
}
private:
Object obj1_;
Object obj2_;
};
int main(void)
{
Container c(10, 20);
return 0;
}
运行结果
Object 10 ...
Object 20 ...
Container ...
~Container ...
~Object 20 ...
~Object 10 ...
从运行结果可看出:
- 构造对象之前,必须
先构造对象的成员
; - 对象成员构造的顺序与
定义时的顺序有关
,跟初始化列表顺序无关; - 如果对象成员对应的类没有默认构造函数,那对象成员也只能在初始化列表进行初始化
如果类是继承而来,基类没有默认构造函数的时候
,基类的构造函数要在派生类构造函数初始化列表中调用。
const成员 引用成员的初始化
#include <iostream>
using namespace std;
class Object
{
public:
enum E_TYPE
{
TYPE_A = 100,
TYPE_B = 200
};
public:
Object(int num = 0) : num_(num), kNum_(num), refNum_(num_)
{
//kNum_ = 100;
//refNum_ = num_;
cout << "Object " << num_ << " ..." << endl;
}
~Object()
{
cout << "~Object " << num_ << " ..." << endl;
}
void DisplayKNum()
{
cout << "kNum=" << kNum_ << endl;
}
private:
int num_;
const int kNum_;
int &refNum_;
};
int main(void)
{
Object obj1(10);
Object obj2(20);
obj1.DisplayKNum();
obj2.DisplayKNum();
cout << obj1.TYPE_A << endl;
cout << obj2.TYPE_A << endl;
cout << Object::TYPE_A << endl;
return 0;
}
必须在构造函数初始化列表中进行初始化:
- const成员的初始化只能在构造函数初始化列表中进行
- 引用成员的初始化也只能在构造函数初始化列表中进行
- 对象成员(对象成员所对应的类
没有默认构造函数
)的初始化,也只能在构造函数初始化列表中进行
因为const变量或者引用都得在定义的时候初始化
,所以const成员和引用成员必须在初始化列表中初始化。可以使用定义枚举类型
来得到类作用域共有的常量。
拷贝构造函数
使用一个已经存在的对象来初始化一个新的同一类型的对象
,只有一个参数并且参数为该类对象的引用 Test::Test(const Test &other)
为什么拷贝构造函数的参数需要是引用? 这是因为如果拷贝构造函数中的参数不是一个引用,那么就相当于采用了传值的方式(pass-by-value),而传值的方式会调用该类的拷贝构造函数,从而造成无穷递归地调用拷贝构造函
#ifndef _TEST_H_
#define _TEST_H_
class Test
{
public:
// 如果类不提供任何一个构造函数,系统将为我们提供一个不带参数的
// 默认的构造函数
Test();
explicit Test(int num);
Test(const Test &other);
void Display();
Test &operator=(const Test &other);
~Test();
private:
int num_;
};
#endif // _TEST_H_
#include "Test.h"
#include <iostream>
using namespace std;
// 不带参数的构造函数称为默认构造函数
Test::Test() : num_(0)
{
//num_ = 0;
cout << "Initializing Default" << endl;
}
Test::Test(int num) : num_(num)
{
//num_ = num;
cout << "Initializing " << num_ << endl;
}
Test::Test(const Test &other) : num_(other.num_)
{
//num_ = other.num_;
cout << "Initializing with other " << num_ << endl;
}
Test::~Test()
{
cout << "Destroy " << num_ << endl;
}
void Test::Display()
{
cout << "num=" << num_ << endl;
}
Test &Test::operator=(const Test &other)
{
cout << "Test::operator=" << endl;
if (this == &other)
return *this;
num_ = other.num_;
return *this;
}
#include "Test.h"
int main(void)
{
Test t(10);
//Test t2(t); // 调用拷贝构造函数
Test t2 = t; // 等价于Test t2(t);
return 0;
}
运行结果
Initializing 10
Initializing with other 10
Destroy 10
Destroy 10
destroy的两个分别是t和t2
拷贝构造函数调用的几种情况
#include "Test.h"
#include <iostream>
using namespace std;
void TestFun(const Test t1)
{
}
void TestFun2(const Test &t1)
{
}
Test TestFun3(const Test &t1)
{
return t1;
}
const Test &TestFun4(const Test &t1)
{
//return const_cast<Test&>(t1);
return t1;
}
函数的形参是类对象
当函数的形参是类的对象调用函数时,进行形参与实参结合时使用。这时要在内存新建立一个局部对象,并把实参拷贝到新的对象中。
int main(void)
{
Test t(10);
TestFun(t);
cout << "........" << endl;
return 0;
}
运行结果
Initializing 10
Initializing with other 10
Destroy 10
........
Destroy 10
参数为引用,即没有调用拷贝构造函数
int main(void)
{
Test t(10);
TestFun2(t);
cout << "........" << endl;
return 0;
}
运行结果
Initializing 10
........
Destroy 10
函数的返回值是类对象
当函数的返回值是类对象,函数执行完成返回调用者时使用。也是要建立一个临时对象,再返回调用者。
为什么不直接用要返回的局部对象呢?
因为局部对象在离开建立它的函数时就消亡了,不可能在返回调用函数后继续生存,所以在处理这种情况时,编译系统会在调用函数的表达式中创建一个无名临时对象
,该临时对象的生存周期只在函数调用处的表达式中。所谓return对象,实际上是调用拷贝构造函数把该对象的值拷入临时对象
。如果返回的是变量,处理过程类似,只是不调用构造函数。
int main(void)
{
Test t(10);
t = TestFun3(t);
cout << "........" << endl;
return 0;
}
运行结果
Initializing 10
Initializing with other 10
Test::operator=
Destroy 10
........
Destroy 10
函数返回时会调用拷贝构造函数
,接着调用赋值运算符,释放临时对象,最后释放t。如果没有用t 接收,不会调用operator= 而且临时对象也会马上释放
int _tmain(void)
{
Test t(10);
Test& t2 = TestFun3(t);
cout << "........" << endl;
return 0;
}
运行结果
Initializing 10
Initializing with other 10
........
Destroy 10
Destroy 10
函数返回时调用拷贝构造函数,因为t2引用着临时对象,故没有马上释放
传参和返回值都没有调用拷贝构造函数
int _tmain(void)
{
Test t(10);
Test t2 = TestFun4(t);
cout << "........" << endl;
return 0;
}
运行结果
Initializing 10
Initializing with other 10
........
Destroy 10
Destroy 10
函数传参和返回都没有调用拷贝构造函数,初始化t2时会调用拷贝构造函数
int _tmain(void)
{
Test t(10);
const Test& t2 = TestFun4(t);
cout << "........" << endl;
return 0;
}
运行结果
Initializing 10
........
Destroy 10
函数传参和返回都没有调用构造函数,t2 是引用故也不会调用拷贝构造函数